﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace QntPlatform.BinanceApi.DApi
{
    public partial class DApiClient
    {

        /**
        symbol	STRING	YES	交易对
        interval	ENUM	YES	时间间隔
        startTime	LONG	NO	起始时间
        endTime	LONG	NO	结束时间
        limit	INT	NO	默认值:500 最大值:1500
        */
        public async Task<IEnumerable<KlineCandleStick>> GetKlines(string symbol, KlineCandleStick.KlineInterval interval, DateTime? startTime = null, DateTime? endTime = null, int? limit = 100)
        {
            if (string.IsNullOrWhiteSpace(symbol))
                throw new ArgumentNullException("symbol", "参数必须");
            var req = CreateReq(Prefix + "klines", false);
            req.AddParameter("symbol", symbol);
            req.AddParameter("interval", interval.ToDescriptionOrString());
            if (startTime != null)
                req.AddParameter("startTime", startTime.Value.Ticks);
            if (endTime != null)
                req.AddParameter("endTime", endTime.Value.Ticks);
            if (limit != null)
                req.AddParameter("limit", limit.Value);
            var list = await req.GetAsync<List<IEnumerable<string>>>().CheckResp(log);
            if (list == null)
                return null;
            var dtoList = new List<KlineCandleStick>();
            foreach (var item in list)
            {
                dtoList.Add(GetKlineDto(item));
            }
            return dtoList.ToArray();
        }
        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        KlineCandleStick GetKlineDto(IEnumerable<string> klineCandlesticks)
        {
            return new KlineCandleStick
            {
                OpenTime = Epoch.AddMilliseconds(Convert.ToInt64(klineCandlesticks.ElementAt(0))),
                Open = Convert.ToDecimal(klineCandlesticks.ElementAt(1)),
                High = Convert.ToDecimal(klineCandlesticks.ElementAt(2)),
                Low = Convert.ToDecimal(klineCandlesticks.ElementAt(3)),
                Close = Convert.ToDecimal(klineCandlesticks.ElementAt(4)),
                Volume = Convert.ToDecimal(klineCandlesticks.ElementAt(5)),
                CloseTime = Epoch.AddMilliseconds(Convert.ToInt64(klineCandlesticks.ElementAt(6))),
                QuoteAssetVolume = Convert.ToDecimal(klineCandlesticks.ElementAt(7)),
                NumberOfTrades = Convert.ToInt32(klineCandlesticks.ElementAt(8)),
                TakerBuyBaseAssetVolume = Convert.ToDecimal(klineCandlesticks.ElementAt(9)),
                TakerBuyQuoteAssetVolume = Convert.ToDecimal(klineCandlesticks.ElementAt(10)),
            };
        }

    }

    [Newtonsoft.Json.JsonConverter(typeof(BinanceExchange.API.Converter.KlineCandleSticksConverter))]
    public class KlineCandleStick : DApiDto
    {
        [DataMember(Order = 1)]
        public DateTime OpenTime { get; set; }

        [DataMember(Order = 2)]
        public decimal Open { get; set; }

        [DataMember(Order = 3)]
        public decimal High { get; set; }

        [DataMember(Order = 4)]
        public decimal Low { get; set; }

        [DataMember(Order = 5)]
        public decimal Close { get; set; }

        [DataMember(Order = 6)]
        public decimal Volume { get; set; }

        [DataMember(Order = 7)]
        public DateTime CloseTime { get; set; }

        [DataMember(Order = 7)]
        public decimal QuoteAssetVolume { get; set; }

        [DataMember(Order = 7)]
        public int NumberOfTrades { get; set; }

        [DataMember(Order = 8)]
        public decimal TakerBuyBaseAssetVolume { get; set; }

        [DataMember(Order = 9)]
        public decimal TakerBuyQuoteAssetVolume { get; set; }

        [JsonConverter(typeof(StringEnumConverter))]
        [DataContract]
        public enum KlineInterval
        {
         
            [DefaultValue(60)] [Description( "1m")] OneMinute,
            [DefaultValue(3 * 60)] [Description( "3m")] ThreeMinutes,
            [DefaultValue(5 * 60)] [Description( "5m")] FiveMinutes,
            [DefaultValue(15 * 60)] [Description( "15m")] FifteenMinutes,
            [DefaultValue(30 * 60)] [Description( "30m")] ThirtyMinutes,
            [DefaultValue(60 * 60)] [Description( "1h")] OneHour,
            [DefaultValue(2 * 60 * 60)] [Description( "2h")] TwoHours,
            [DefaultValue(4 * 60 * 60)] [Description( "4h")] FourHours,
            [DefaultValue(6 * 60 * 60)] [Description( "6h")] SixHours,
            [DefaultValue(8 * 60 * 60)] [Description( "8h")] EightHours,
            [DefaultValue(12 * 60 * 60)] [Description( "12h")] TwelveHours,
            [DefaultValue(24 * 60 * 60)] [Description( "1d")] OneDay,
            [DefaultValue(3 * 24 * 60 * 60)] [Description( "3d")] ThreeDays,
            [DefaultValue(7 * 24 * 60 * 60)] [Description( "1w")] OneWeek,
            [DefaultValue(30 * 24 * 60 * 60)] [Description( "1M")] OneMonth
        }
        public static KlineInterval PeriodConvert(int period)
        {
            switch (period)
            {
                case 60:
                    return KlineInterval.OneMinute;
                case 3 * 60:
                    return KlineInterval.ThreeMinutes;
                case 5 * 60:
                    return KlineInterval.FiveMinutes;
                case 15 * 60:
                    return KlineInterval.FifteenMinutes;
                case 30 * 60:
                    return KlineInterval.ThirtyMinutes;
                case 3600: return KlineInterval.OneHour;
                case 2 * 3600: return KlineInterval.TwoHours;
                case 4 * 3600: return KlineInterval.FourHours;
                case 6 * 3600: return KlineInterval.SixHours;
                case 8 * 3600: return KlineInterval.EightHours;
                case 12 * 3600: return KlineInterval.TwelveHours;
                case 24 * 3600: return KlineInterval.OneDay;
                case 3 * 24 * 3600: return KlineInterval.ThreeDays;
                case 7 * 24 * 3600: return KlineInterval.OneWeek;
                case 30 * 24 * 3600: return KlineInterval.OneMonth;
                default:
                    throw new ArgumentException();
            }
        }
    }

}
